package wechat

import (
	"crypto/sha1"
	"encoding/xml"
	"fmt"
	"io"
	"log"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	MsgTypeText     = "text"  //文本
	MsgTypeImage    = "image" //图片
	MsgTypeVoice    = "voice" //语言
	MsgTypeVideo    = "video" //视频
	MsgtypeMusic    = "music" //音乐
	MsgTypeArticles = "news"  //图文

)
const (
	KeyPrdfix = "qrscene_" //接收到微信key的前缀
	KeyProxy  = "proxy_"   //代理人,如果信息前缀是这个,那么是推荐信息
	KeyLogin  = "login_"   //登陆的二维码信息,虽然没用,但预留着
)

type MediaId struct {
	MediaId string
}
type WxBase struct {
	ToUserName   string
	FromUserName string
	CreateTime   int64
	MsgType      string
	XMLName      xml.Name `xml:"xml"`
}

type WXRepTextMsg struct {
	WxBase
	Content string
}

// xml图片结构体
type WXImg struct {
	WxBase
	Image MediaId
}

type WGzh struct {
	Signature string `json:"signature"  form:"signature"`
	Nonce     string `json:"nonce" form:"nonce"`
	Timestamp string `json:"timestamp" form:"timestamp"`
	Echostr   string `json:"echostr" form:"echostr"`
	Token     string `json:"token" form:"token"`
}

// 微信公众号消息,
type WechatMsg struct {
	//基本消息
	ToUserName   string
	FromUserName string
	CreateTime   int64
	MsgType      string //文本text,图片image,语言voice,视频video,小视频shortvideo,位置location,链接link,事件event,
	MsgId        int64

	//文本消息
	Content string //文本消息内容
	Status  string //发送状态

	//图文
	PicUrl  string
	MediaId string //图片,视频,小视频ID

	//语音如果带语音识别
	Recognition string //识别后的结果
	Format      string //语音的格式

	//位置消息
	Location_X float64
	Location_Y float64
	Scale      int    //地图缩放大小
	Label      string //位置信息

	//链接消息
	Title       string
	Description string
	Url         string

	//事件消息
	Event    string //消息类型,subscribe订阅,unsubscribe取消订阅,CLICK点击菜单,VIEW
	EventKey string //扫码带参,qrscene_123123 qrscene_为前缀，后面为二维码的参数值,
	Ticket   string //二维码的ticket，可用来换取二维码图片

}

func (a *C微信链接) MakeSignature(gzh *WGzh) string {
	//1. 将 plat_token、timestamp、nonce三个参数进行字典序排序
	sl := []string{a.Token, gzh.Timestamp, gzh.Nonce}
	sort.Strings(sl)
	//2. 将三个参数字符串拼接成一个字符串进行sha1加密
	s := sha1.New()
	io.WriteString(s, strings.Join(sl, ""))
	return fmt.Sprintf("%x", s.Sum(nil))
}

// 接收到文本信息
func RText(wechatmsg *WechatMsg, c *gin.Context) {
	msg := "收到您的消息啦，您可以尝试联系客服微信："
	WXMsgText(wechatmsg, msg, c)
}

/*
func F微信公众号服务(c *gin.Context) {
	var wx WGzh
	err := c.ShouldBindQuery(&wx)
	if err != nil {
		c.String(http.StatusFound, "err")
		c.Abort()
		return
	}
	//wx.Token=   //这个需要从配置里面获取
	cli := F新建微信链接("", "")
	str := cli.MakeSignature(&wx) //排序后sha1加密生成比对信息
	//plug.Println(wx, "str:", str)
	//如果验证信息有问题,那么报错
	if wx.Signature != str {
		c.String(http.StatusFound, "err")
		c.Abort()
		return
	}

	//如果是验证信息,那么就返回正确
	//plug.Printf("signatureGen=%s,signatureIn=%s\n", wx.Signature, str)
	if wx.Signature == str && wx.Echostr != "" {
		c.String(http.StatusOK, wx.Echostr)
		return
	}

	//如果类型是post并且验证信息正常,那么开始进入
	if c.Request.Method == "POST" && wx.Signature == str {
		var wxmsg WechatMsg
		err := c.ShouldBindXML(&wxmsg)
		//plug.Printf("[消息接收] - 收到消息, 消息类型为: %s, 消息内容为: %s\n", wxmsg.MsgType, wxmsg.Content)

		if err != nil {
			c.String(http.StatusFound, "err")
			c.Abort()
			return
		}
		//文本text,图片image,语言voice,视频video,小视频shortvideo,位置location,链接link,事件event,
		switch wxmsg.MsgType {
		case "text":
			//fmt.Printf("正在进入图片的消息")
			RText(&wxmsg, c)
		case "event": //扫码点击事件消息
			REvent(&wxmsg, c)
		default:
			c.String(http.StatusOK, "") //默认不返回消息
		}

	}

} */

// 事件
func REvent(wechatmsg *WechatMsg, c *gin.Context) {
	//util.SetCache("wechatmsg", wechatmsg)
	//如果二维码信息不为空,那么就是用户扫码关注了的
	if wechatmsg.Ticket != "" {
		fmt.Printf("用户扫码关注了%+v", wechatmsg)

		//先看值的前面是否有qrscene_
		nn := strings.Index(wechatmsg.EventKey, KeyPrdfix)
		if nn == 0 {
			wechatmsg.EventKey = wechatmsg.EventKey[len(KeyPrdfix):]
		}
		//EventKey,暂时返回扫码时的参数..
		//后期可以带上参数,用户登陆二维码,用户推广二维码直接注册绑定上级

		//是上级的推荐码扫码进来的
		nn = strings.Index(wechatmsg.EventKey, KeyProxy)
		if nn == 0 {
			wechatmsg.EventKey = wechatmsg.EventKey[len(KeyProxy):]
			fmt.Println("是上级的推荐码扫码进来的")
			//开始查询用户信息,并且绑定上级信息注册
			//根据openid查找用户信息,如果无用户信息,那么注册
		}
		//用户扫码登陆的
		nn = strings.Index(wechatmsg.EventKey, KeyLogin)
		if nn == 0 {
			wechatmsg.EventKey = wechatmsg.EventKey[len(KeyLogin):]
			fmt.Println("用户扫码登陆的")
		}
		//生成的key可以在前面带上登陆还是推广二维码
		WXMsgText(wechatmsg, wechatmsg.EventKey, c)
		return
	}

	//第一次关注
	switch wechatmsg.Event {
	case "unsubscribe": //取消关注
		fmt.Println("取消关注", wechatmsg)
	case "subscribe": //第一次关注
		//查询一下有没有这个用户，如果没有，那么新建一下，如果有，那么更新为已关注
		/* var user ThirdUser
		if err := dao.MDB.Where("openid = ?", wechatmsg.FromUserName).Last(&user).Error; err != nil {
			if err.Error() != config.Err数据搜索不存在 {
				WXMsgText(wechatmsg, "感谢您的关注", c)
				return
			}
			//新建一个已经关注的第三方用户
			user.Id = plug.Sid.F获取id()
			user.Appid = wechatmsg.ToUserName
			user.Openid = wechatmsg.FromUserName
			user.Iscare = 1
			dao.MDB.Create(&user)
		} else {
			if user.Uid != 0 {
				dao.MDB.Model(&User{}).
					Where("id = ?", user.Uid).
					Update("iscare", 1)
			}
		} */
		WXMsgText(wechatmsg, "感谢您的关注", c)
	case "SCAN": // 用户已关注时扫码进入,实际上面已经有了,这里不做判断
		c.String(http.StatusOK, "")
	case "CLICK": //用户点击菜单事件,事件KEY值，与自定义菜单接口中KEY值对应
		WXMsgText(wechatmsg, wechatmsg.EventKey, c)
	case "VIEW": //点击菜单跳转链接时的事件推送,	事件KEY值，设置的跳转URL
		WXMsgText(wechatmsg, wechatmsg.EventKey, c)
	case "TEMPLATESENDJOBFINISH": //点击菜单跳转链接时的事件推送,	事件KEY值，设置的跳转URL模板消息发送
		c.String(http.StatusOK, "")
	default:
		c.String(http.StatusOK, "")
	}

}

// 微信图片消息发送,wechatmsg收到的用户信息,mediaid图片资源id
func RImage(wechatmsg *WechatMsg, mediaid string, c *gin.Context) {
	img := MediaId{MediaId: mediaid}
	Rep := WXImg{
		WxBase: GetBaseInfo(wechatmsg, MsgTypeImage),
		Image:  img,
	}
	msg, err := xml.Marshal(&Rep)
	//fmt.Printf("进入图片推送%+v\n msg的消息是%+v", Rep, msg)
	if err != nil {
		log.Printf("[消息回复] - 将对象进行XML编码出错: %v\n", err)
		return
	}
	_, _ = c.Writer.Write(msg)
}

func GetBaseInfo(wechatmsg *WechatMsg, msgtype string) WxBase {
	//fmt.Printf("msgtype%+v", msgtype)
	base := WxBase{
		ToUserName:   wechatmsg.FromUserName,
		FromUserName: wechatmsg.ToUserName,
		CreateTime:   time.Now().Unix(),
		MsgType:      msgtype,
	}
	return base
}

// 返回文本
func WXMsgText(wechatmsg *WechatMsg, msginfo string, c *gin.Context) {
	repTextMsg := WXRepTextMsg{
		WxBase:  GetBaseInfo(wechatmsg, MsgTypeText),
		Content: msginfo,
	}
	msg, err := xml.Marshal(&repTextMsg)
	if err != nil {
		//log.Printf("[消息回复] - 将对象进行XML编码出错: %v\n", err)
		return
	}
	_, _ = c.Writer.Write(msg)
}
